package com.czh.service.impl;

import com.czh.service.AsyncService;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;

/**
 * @author chenzhihua
 * @Date 2024/8/7
 */
@Service
@Slf4j
public class AsyncServiceImpl implements AsyncService {
    @Resource
    @Qualifier("taskExecutor")
    private Executor taskExecutor;

    // 处理线程安全问题
    @Resource
    private RedissonClient redissonClient;

    private RLock lock;

    @Resource
    private AsyncService2Impl asyncService2;

    @PostConstruct
    public void init() {
        // 初始化分布式锁
        this.lock = redissonClient.getLock("inventoryLock");
    }

    /**
     * 注意：如果要使用多线程: 注册和发邮件不能在同一个类下
     * 这边: modelRegisterUser方法在asyncService中、asyncMethod方法在asyncService2中
     * 执行注册用户的逻辑: 10 http-nio-8080-exec-3
     * 执行一个异步方法发送邮件: pool-1-thread-2
     */
    @Override
    public void registerUser() {
        // 执行业务
        boolean b = modelRegisterUser();
        // 注册成功才进行发送邮件
        if (b) {
            // 异步执行发送给邮件
            asyncService2.asyncMethod();
        } else {
            System.out.println("注册失败");
        }
    }

    private boolean modelRegisterUser() {
        // 模拟用户注册逻辑
        try {
            Random random = new Random();
            // 模拟是否注册成功
            int num = random.nextInt(11);
            System.out.println();
            log.info("随机数是：{},执行注册用户的逻辑: {}", num, Thread.currentThread().getName());
            return num > 1;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 定义一个异步方法
     *
     * @Async("taskExecutor") 指定线程池名称是：taskExecutor
     */
    @Async("taskExecutor")
    @Override
    public void asyncMethod() {
        log.info("执行一个异步方法发送邮件: {}", Thread.currentThread().getName());
    }

    /**
     * 并发请求处理
     */
    // 使用taskExecutor.execute()实现多线程
    @Override
    public void concurrentRequest() {
        // 执行业务代码
        taskExecutor.execute(() -> {
            boolean b = decreaseStock();
            if (b) {
                log.info("执行下订单的业务代码: {}", Thread.currentThread().getName());
            } else {
                log.error("执行下订单异常");
            }
        });
    }


    // run方法执行的具体代码，使用Redission解决线程安全问题
    private boolean decreaseStock() {
        try {
            // 获取锁，最多等待3s，锁定10s
            lock.tryLock(3, 10, TimeUnit.SECONDS);
            // 执行判断业务
            log.info("扣减库存成功：{}", Thread.currentThread().getName());
            return true;
        } catch (InterruptedException e) {
            // 关闭当前线程
            Thread.currentThread().interrupt();
            log.info("扣减线程被中断: {}", Thread.currentThread().getName());
            return false;
        } finally {
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock(); // 释放锁
            }
        }
    }

    @Override
    @Async("taskExecutor")
    public CompletableFuture<List<String>> parallelComputing() {
        // 执行并行的计算
        CompletableFuture<Double> doubleCompletableFuture = asyncService2.calculatePrice(Arrays.asList(2.0, 1.2, 1.0));
        CompletableFuture<List<String>> listCompletableFuture = asyncService2.generateRecommendations(1);
        // 等待所有任务完成，并处理返回值
        return CompletableFuture.allOf(doubleCompletableFuture, listCompletableFuture)
                .thenApply(v -> {
                    Double price = doubleCompletableFuture.join();
                    log.info("价格是：{}", price);
                    List<String> recommendations = listCompletableFuture.join();
                    log.info("并行计算中推荐的商品是：{}", recommendations);
//                    recommendations.add(price.toString());
                    return recommendations;
                });
    }

}
